import logging
from typing import List

from redis.asyncio import Redis
from sqlalchemy import func, or_, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.core.config import Settings
from app.exceptions.user import UserDuplicateException, UserNotFoundException
from app.models.system import SysDept, SysRole, SysUser, SysUserRole
from app.schemas.base import Pagination
from app.schemas.option import StringOption
from app.schemas.user import UserForm, UserPageQuery, UserPageView, UserUpdate
from app.service.user_role_service import UserRoleService

logger = logging.getLogger(__name__)


class UserService:
    """
    用户服务：用户信息管理
    """

    def __init__(self, redis: Redis, session: AsyncSession, settings: Settings):
        self.redis = redis
        self.session = session
        self.settings = settings

    async def list_user_page(self, query_params: UserPageQuery) -> Pagination[UserPageView]:
        """
        分页查询用户列表

        实现多条件用户查询功能，支持关键词搜索、状态过滤、部门过滤、时间范围过滤、
        角色过滤等多种查询条件，并返回分页结果。

        Args:
            query_params (UserPageQuery): 分页查询参数对象，包含以下字段：
                - page_num: 页码，默认1
                - page_size: 每页记录数，默认10
                - keywords: 关键词搜索（用户名/昵称/手机号模糊匹配）
                - status: 用户状态过滤（1-正常 0-禁用）
                - dept_id: 部门ID过滤（包含该部门及其子部门）
                - role_ids: 角色ID列表过滤
                - create_time: 创建时间范围过滤 [开始日期, 结束日期]
                - field: 排序字段（create_time/update_time）
                - direction: 排序方向（ASC/DESC）
                - is_root: 是否排除超级管理员用户

        Returns:
            UserPageResult: 结果对象，包含：
                - total: 符合条件的总记录数
                - items: 当前页的用户数据列表
        """
        # 构建基础查询语句，排除已删除的用户（逻辑删除标识为0表示未删除）
        stmt = select(SysUser).where(SysUser.is_deleted == 0)

        # 如果查询参数指定排除root用户，则添加子查询条件
        # 通过检查用户是否拥有管理员角色来识别root用户
        if not query_params.is_root:
            subquery_role = (
                select(SysUserRole.role_id)
                .join(SysRole, SysRole.id == SysUserRole.role_id)
                .where(SysRole.code == self.settings.ADMIN_ROLE_CODE)
                .where(SysUserRole.user_id == SysUser.id)
                .exists()
            )
            stmt = stmt.where(~subquery_role)

        # 关键词搜索：支持用户名、昵称、手机号模糊匹配
        # 使用ilike进行不区分大小写的模糊查询，%表示任意字符匹配
        if query_params.keywords:
            stmt = stmt.where(
                or_(
                    SysUser.username.ilike(func.concat("%", query_params.keywords, "%")),
                    SysUser.nickname.ilike(func.concat("%", query_params.keywords, "%")),
                    SysUser.mobile.ilike(func.concat("%", query_params.keywords, "%")),
                )
            )

        # 状态过滤：按用户状态进行筛选（1-正常 0-禁用）
        if query_params.status:
            stmt = stmt.where(SysUser.status == query_params.status)

        # 部门过滤：支持部门及其子部门的用户查询
        # 通过tree_path字段实现部门层级查询，包含指定部门的所有子部门
        if query_params.dept_id:
            stmt = stmt.where(
                SysUser.dept_id.in_(
                    select(SysDept.id)
                    .where(
                        func.concat(",", func.coalesce(SysDept.tree_path, ""), ",", SysDept.id, ",").like(
                            func.concat("%,", query_params.dept_id, ",%")
                        )
                    )
                    .distinct()
                )
            )

        # 创建时间范围过滤
        if query_params.create_time and len(query_params.create_time) > 0:
            if len(query_params.create_time) == 1:
                # 只有一个时间点，查询该时间点之后的记录
                stmt = stmt.where(func.date(SysUser.create_time) >= query_params.create_time[0])
            elif len(query_params.create_time) == 2:
                # 有两个时间点，查询时间范围内的记录
                stmt = stmt.where(
                    func.date(SysUser.create_time).between(query_params.create_time[0], query_params.create_time[1])
                )

        # 角色过滤：按角色ID列表筛选用户
        # 通过用户-角色关联表进行多对多关系查询
        if query_params.role_ids and len(query_params.role_ids) > 0:
            stmt = stmt.where(
                SysUser.id.in_(
                    select(SysUserRole.user_id).where(SysUserRole.role_id.in_(query_params.role_ids)).distinct()
                )
            )

        # 预加载部门信息以提高查询性能
        # 使用selectinload避免N+1查询问题，一次性加载关联的部门数据
        stmt = stmt.options(selectinload(SysUser.dept), selectinload(SysUser.roles))

        # 计算分页偏移量：当前页码减1乘以每页大小
        offset = (query_params.page_num - 1) * query_params.page_size

        # 总数查询（不带 limit/offset）
        # 先获取符合条件的总记录数，用于分页信息展示
        total_query = select(func.count()).select_from(stmt.subquery())
        result = await self.session.execute(total_query)
        total = result.scalar()

        # 数据排序：支持自定义字段和方向排序，默认按创建时间和更新时间倒序
        if (
            query_params.field
            and len(query_params.field) > 0
            and query_params.direction
            and hasattr(SysUser, query_params.field)
        ):
            # 动态获取排序字段，支持自定义排序
            field = getattr(SysUser, query_params.field)
            if query_params.direction.lower() == "desc":
                stmt.order_by(field.desc())
            else:
                stmt.order_by(field.asc())
        else:
            # 默认排序：按创建时间和更新时间倒序，最新数据优先显示
            stmt = stmt.order_by(SysUser.create_time.desc(), SysUser.update_time.desc())

        # 应用分页限制：设置偏移量和限制数量
        paginated_query = stmt.offset(offset).limit(query_params.page_size)
        result = await self.session.execute(paginated_query)
        _users = result.scalars().all()

        users = [
            UserPageView(
                id=user.id,
                username=user.username,
                nickname=user.nickname,
                mobile=user.mobile,
                gender=user.gender,
                avatar=user.avatar,
                status=user.status,
                email=user.email,
                dept_name=user.dept.name if user.dept else None,
                role_names=",".join([role.name for role in user.roles]) if user.roles else None,
                create_time=user.create_time,
            )
            for user in _users
        ]

        return Pagination[UserPageView](
            total=total, list=users, page=query_params.page_num, size=query_params.page_size
        )

    async def save_user(self, user: SysUser) -> SysUser:
        """
        保存用户信息
        Args:
            user (SysUser): 用户信息对象
        Returns:
            SysUser: 保存后的用户信息对象
        """
        self.session.add(user)
        await self.session.commit()
        await self.session.refresh(user)
        return user

    async def save_user_with_roles(self, user: SysUser, role_ids: list[int]) -> SysUser:
        """
        保存用户信息及其角色关联（事务一致性）

        Args:
            user (SysUser): 用户信息对象
            role_ids (list[int]): 角色ID列表

        Returns:
            SysUser: 保存后的用户信息对象
        """
        # 开始事务：保存用户
        self.session.add(user)
        await self.session.flush()  # 获取用户ID但不提交事务

        if role_ids:
            # 保存用户角色关联
            user_role_service = UserRoleService(self.session)
            await user_role_service.save_user_roles(user_id=user.id, role_ids=role_ids, is_commit=False)

        # 提交事务
        await self.session.commit()
        await self.session.refresh(user)
        return user

    async def get_user_byid(self, user_id: int) -> SysUser:
        """
        根据ID获取用户信息
        Args:
            user_id (int): 用户ID
        Returns:
            SysUser: 用户信息对象
        """
        stmt = (
            select(SysUser)
            .where(SysUser.id == user_id)
            .options(selectinload(SysUser.dept), selectinload(SysUser.roles))
        )
        result = await self.session.execute(stmt)
        user = result.scalar_one_or_none()
        return user

    async def get_user_by_username(self, username: str) -> SysUser:
        """
        根据用户名获取用户信息
        Args:
            username (str): 用户名
        Returns:
            SysUser: 用户信息对象
        """
        stmt = (
            select(SysUser)
            .where(SysUser.username == username)
            .options(selectinload(SysUser.dept), selectinload(SysUser.roles))
        )
        result = await self.session.execute(stmt)
        user = result.scalar_one_or_none()
        return user

    async def update_user(self, user_id: int, user_update: UserUpdate) -> SysUser:
        """
        更新用户信息
        Args:
            user_id (int): 用户ID
            user_update (UserUpdate): 更新用户信息的表单对象
        Returns:
            SysUser: 更新后的用户信息对象
        """
        stmt = select(SysUser).where(SysUser.id == user_id)
        result = await self.session.execute(stmt)
        user = result.scalar_one_or_none()
        if not user:
            raise UserNotFoundException()
        # 更新用户信息
        for key, value in user_update.model_dump(exclude_unset=True).items():
            setattr(user, key, value)
        await self.session.commit()
        await self.session.refresh(user)
        return user

    async def update_user_with_roles(self, user_form: UserForm) -> SysUser:
        """
        更新用户信息
        Args:
            user_update (UserUpdate): 更新用户信息的表单对象
        Returns:
            SysUser: 更新后的用户信息对象
        """
        # 检查用户是否存在
        if user_form.username:
            stmt = select(SysUser).where(SysUser.id != user_form.id, SysUser.username == user_form.username)
            result = await self.session.execute(stmt)
            user = result.scalar_one_or_none()
            if user:
                raise UserDuplicateException()

        stmt = select(SysUser).where(SysUser.id == user_form.id)
        result = await self.session.execute(stmt)
        user = result.scalar_one_or_none()
        if not user:
            raise UserNotFoundException()
        # 更新用户信息
        for key, value in user_form.model_dump(exclude_unset=True, exclude=["id", "role_ids"]).items():
            # 非 SysUser 模型字段的更新
            if key not in user.__dict__:
                continue
            setattr(user, key, value)

        user.update_time = func.now()
        user.update_by = user_form.op_id

        if user_form.role_ids:
            # 保存用户角色关联
            user_role_service = UserRoleService(self.session)
            await user_role_service.save_user_roles(user_id=user.id, role_ids=user_form.role_ids, is_commit=False)
        await self.session.commit()
        await self.session.refresh(user)
        return user

    async def delete_users(self, op_id: int, user_ids: List[int]) -> bool:
        """
        删除用户信息(逻辑删除)
        Args:
            op_id (int): 操作人ID
            user_ids (List[int]): 用户ID列表
        Returns:
            bool: 删除成功返回True，否则返回False
        """
        stmt = (
            update(SysUser)
            .where(SysUser.id.in_(user_ids))
            .values(is_deleted=1, update_time=func.now(), update_by=op_id)
        )
        result = await self.session.execute(stmt)
        await self.session.commit()
        return result.rowcount > 0

    async def update_user_status(self, op_id: int, user_id: int, status: int) -> bool:
        """
        更新用户状态
        Args:
            op_id (int): 操作人ID
            user_id (int): 用户ID
            status (int): 用户状态（1-正常 0-禁用）
        Returns:
            bool: 更新成功返回True，否则返回False
        """
        stmt = (
            update(SysUser).where(SysUser.id == user_id).values(status=status, update_time=func.now(), update_by=op_id)
        )
        result = await self.session.execute(stmt)
        await self.session.commit()
        return result.rowcount > 0

    async def list_user_options(self, is_root: bool = False) -> List[StringOption]:
        """
        获取用户选项列表
        Args:
            is_root (bool): 是否排除超级管理员用户，默认为False
        Returns:
            List[StringOption]: 用户选项列表
        """
        stmt = select(SysUser).where(SysUser.is_deleted == 0, SysUser.status == 1)
        if not is_root:
            subquery_role = (
                select(SysUserRole.role_id)
                .join(SysRole, SysRole.id == SysUserRole.role_id)
                .where(SysRole.code == self.settings.ADMIN_ROLE_CODE)
                .where(SysUserRole.user_id == SysUser.id)
                .exists()
            )
            stmt = stmt.where(~subquery_role)
        result = await self.session.execute(stmt)
        users = result.scalars().all()
        options = [StringOption.create_simple(value=user.id, label=user.nickname) for user in users]
        return options
